package com.joinway.framework.support.logging.service.impl;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.joinway.framework.bean.logging.type.EventLevel;
import com.joinway.framework.support.logging.service.IEventHandler;
import com.joinway.framework.support.logging.service.IEventLogService;

public class EventLogService extends AbstractLogService implements IEventLogService {

	private final static Logger log = LoggerFactory.getLogger(EventLogService.class);

	protected DataSource dataSource;
	
	protected IEventHandler eventHandler;
	
	protected String table = "event_log";
	
	@Override
	public String getTable() {
		return table;
	}

	@Override
	public void info(String id) {
		this.saveRow(id, EventLevel.Info, null, null, null, null, null);
	}

	@Override
	public void info(String id, String message) {
		this.saveRow(id, EventLevel.Info, message, null, null, null, null);
	}

	@Override
	public void info(String id, String message, String key) {
		this.saveRow(id, EventLevel.Info, message, key, null, null, null);
	}

	@Override
	public void info(String id, String message, String key, String keyType) {
		this.saveRow(id, EventLevel.Info, message, key, keyType, null, null);
	}

	@Override
	public void info(String id, String message, String key, String keyType, String user) {
		this.saveRow(id, EventLevel.Info, message, key, keyType, user, null);
	}

	@Override
	public void warn(String id) {
		this.saveRow(id, EventLevel.Warn, null, null, null, null, null);
	}

	@Override
	public void warn(String id, String message) {
		this.saveRow(id, EventLevel.Warn, message, null, null, null, null);
	}

	@Override
	public void warn(String id, String message, String key) {
		this.saveRow(id, EventLevel.Warn, message, key, null, null, null);
	}

	@Override
	public void warn(String id, String message, String key, String keyType) {
		this.saveRow(id, EventLevel.Warn, message, key, keyType, null, null);
	}

	@Override
	public void warn(String id, String message, String key, String keyType, String user) {
		this.saveRow(id, EventLevel.Warn, message, key, keyType, user, null);
	}

	@Override
	public void error(String id) {
		this.saveRow(id, EventLevel.Error, null, null, null, null, null);
	}

	@Override
	public void error(String id, Throwable t){
		this.saveRow(id, EventLevel.Error, null, null, null, null, t);
	}
	
	@Override
	public void error(String id, String message) {
		this.saveRow(id, EventLevel.Error, message, null, null, null, null);
	}

	@Override
	public void error(String id, String message, Throwable t) {
		this.saveRow(id, EventLevel.Error, message, null, null, null, t);
	}

	@Override
	public void error(String id, String message, String key) {
		this.saveRow(id, EventLevel.Error, message, key, null, null, null);
	}

	@Override
	public void error(String id, String message, String key, Throwable t) {
		this.saveRow(id, EventLevel.Error, message, key, null, null, t);
	}

	@Override
	public void error(String id, String message, String key, String keyType) {
		this.saveRow(id, EventLevel.Error, message, key, keyType, null, null);
	}

	@Override
	public void error(String id, String message, String key, String keyType, Throwable t) {
		this.saveRow(id, EventLevel.Error, message, key, keyType, null, t);
	}

	@Override
	public void error(String id, String message, String key, String keyType, String user) {
		this.saveRow(id, EventLevel.Error, message, key, keyType, user, null);
	}

	@Override
	public void error(String id, String message, String key, String keyType, String user, Throwable t) {
		this.saveRow(id, EventLevel.Error, message, key, keyType, user, t);
	}

	@Override
	public void fatal(String id){
		this.saveRow(id, EventLevel.Fatal, null, null, null, null, null);
	}
	
	@Override
	public void fatal(String id, Throwable t){
		this.saveRow(id, EventLevel.Fatal, null, null, null, null, t);
	}
	
	@Override
	public void fatal(String id, String message) {
		this.saveRow(id, EventLevel.Fatal, message, null, null, null, null);
	}

	@Override
	public void fatal(String id, String message, Throwable t) {
		this.saveRow(id, EventLevel.Fatal, message, null, null, null, t);
	}

	@Override
	public void fatal(String id, String message, String key) {
		this.saveRow(id, EventLevel.Fatal, message, key, null, null, null);
	}

	@Override
	public void fatal(String id, String message, String key, Throwable t) {
		this.saveRow(id, EventLevel.Fatal, message, key, null, null, t);
	}

	@Override
	public void fatal(String id, String message, String key, String keyType) {
		this.saveRow(id, EventLevel.Fatal, message, key, keyType, null, null);
	}

	@Override
	public void fatal(String id, String message, String key, String keyType, Throwable t) {
		this.saveRow(id, EventLevel.Fatal, message, key, keyType, null, t);
	}

	@Override
	public void fatal(String id, String message, String key, String keyType, String user) {
		this.saveRow(id, EventLevel.Fatal, message, key, keyType, user, null);
	}

	@Override
	public void fatal(String id, String message, String key, String keyType, String user, Throwable t) {
		this.saveRow(id, EventLevel.Fatal, message, key, keyType, user, t);
	}

	protected void saveRow(String id, EventLevel level, String message, String key, String keyType, String user, Throwable t){
		Map<String, Object> row = new HashMap<String, Object>();
		
		row.put("event_id", id);
		
		if(level != null){
			row.put("event_level", level.ordinal());
		}
		
		if(StringUtils.isNotBlank(key)){
			row.put("event_key", key);
		}
		
		if(StringUtils.isNotBlank(keyType)){
			row.put("key_type", keyType);
		}
		
		if(StringUtils.isNotBlank(message)){
			row.put("message", message);
		}
		
		if(StringUtils.isNotBlank(user)){
			row.put("user", user);
		}else{
			row.put("user", getUser());
		}
		
		if(t != null){
			StringWriter sw = new StringWriter();
			t.printStackTrace(new PrintWriter(sw));
			row.put("error_trace", sw.toString());
			
			if(row.get("message") == null || StringUtils.isBlank(String.valueOf(row.get("message")))){
				row.put("message", t.getMessage());
			}
		}
		
		Date today = new Date();
		row.put("event_time", DateFormatUtils.format(today, "yyyyMMddHHmmss"));
		row.put("create_date", today);

		super.save(row);
		
		handleEvent(level, row);
	}

	protected void handleEvent(EventLevel level, Map<String, Object> row){
		if(eventHandler == null) return;
		
		try{
			if(level == EventLevel.Error){
				eventHandler.handleErrorEvent(row);
			}else if(level == EventLevel.Fatal){
				eventHandler.handleFatalEvent(row);
			}else if(level == EventLevel.Warn){
				eventHandler.handleWarnEvent(row);
			}else{
				eventHandler.handleInfoEvent(row);
			}
		}catch(Exception e){
			log.error("failed to handle event " + row.get("event_id") + ", event level " + level, e);
		}
	}
	
	protected String getUser(){
		return "system";
	}
	
	public IEventHandler getEventHandler() {
		return eventHandler;
	}

	public void setEventHandler(IEventHandler eventHandler) {
		this.eventHandler = eventHandler;
	}

	public void setTable(String table) {
		this.table = table;
	}
}

